import { print_stacks } from "./Tools";

/**
 * hook 所有重载的函数
 * 基本功能：打印其参数，返回值
 * 拓展功能: 屏蔽函数
 * 已知问题：多线程会打印错乱
 * @param {String} clsName 完整类名
 * @param {String} methodName 完整方法名
 * @param {Boolean} isBlock 是否屏蔽该函数，默认不屏蔽
 */
function traceMethod(clsName, methodName, isBlock = false) {
    var cls = Java.classFactory.use(clsName);
    var overloadCount = cls[methodName].overloads.length;
    const fullMethodName = clsName + "." + methodName
    console.log("Tracing " + fullMethodName + " [" + overloadCount + " overload(s)]");
    for (let i = 0; i < overloadCount; i++) {
        //hook每一个重载
        cls[methodName].overloads[i].implementation = function () {
            // TODO: 利用反射打印重载函数参数类型
            console.warn("[+]*** entered " + fullMethodName + " [" + i + "]");
            //可以打印每个重载的调用栈，对调试有巨大的帮助，当然，信息也很多，尽量不要打印，除非分析陷入僵局
            print_stacks()
            if (methodName == "$init") {
                // print_stacks()
            }
            // 打印参数
            // if (arguments.length) console.log();
            for (let j = 0; j < arguments.length; j++) {
                // 参数类型
                let argClasName = null
                try {
                    argClsName = arguments[j].$className
                } catch (e) {
                    // 对于基本数据类型可能会报错
                }
                if (argClasName) {

                }

                console.log("arg[" + j + "]: " + arguments[j] + "\t<" + arguments[j].$className + ">");
            }
            if (isBlock) {
                console.warn("[-]*** block " + fullMethodName);
                return null
            } else {
                //打印返回值
                var retval = cls[methodName].overloads[i].apply(this, arguments); // rare crash (Frida bug?) 不加 overloads 会崩溃
                console.log("retval:\t" + retval);
                console.warn("[-]*** exiting " + fullMethodName);
                // debugger
                return retval;
            }

        }
    }
}

/**
 * hook 类中所有函数(含构造器)
 * 输出其参数，返回值
 * @param {String} clsName
 */
function traceClass(clsName) {
    const hook = Java.classFactory.use(clsName);
    const methods = hook.class.getDeclaredMethods() // 拿到当前类的所有方法
    hook.$dispose
    let parsedMethods = new Set()
    methods.forEach(method => {
        parsedMethods.add(method.getName())
    });
    parsedMethods.add("$init")
    parsedMethods.forEach((k, v) => {
        traceMethod(clsName, k)
    })
}

/**
 * 按关键词 根据 include 原则搜索类，将找到的Class Hook
 * 如输入完整类名，可追踪包含其所有内部类
 * 
 * @param {string} clsNameKeyWord 
 */
function search_and_trace_classes(clsNameKeyWord) {
    Java.enumerateLoadedClasses({
        "onMatch": function (name, handle) {
            if (name.includes(clsNameKeyWord)) {
                traceClass(name)
            }
        },
        "onComplete": function () {
            console.log("=== search", clsNameKeyWord, "done ===");
        }
    });
}

/**
 * hook native 导出函数函数
 * @param {Module} impl 导出函数对应 Module
 * @param {String} name 导出函数名称
 */
function traceModule(impl, name) {
    console.log("Tracing " + name);
    Interceptor.attach(impl, {
        onEnter: function (args) {
            console.warn("[+] entered " + name);
            console.log("Backtrace:\n" +
                Thread.backtrace(this.context, Backtracer.ACCURATE)
                    .map(DebugSymbol.fromAddress).join("\n"));
        },
        onLeave: function (retval) {
            console.log("retval: " + retval);
            console.warn("[-] exiting " + name);
        }
    });
}

export { traceMethod, traceClass, traceModule, search_and_trace_classes }